﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using TomanuExtensions;
using RaytracerLib.MathLib.Polynomials;

namespace RaytracerLib.MathLib.RootFinders
{
    public class NewtonRootFinder : DerivativeRootFinder
    {
        public override double FindRoot(double a_a, double a_b, 
            Func<double, double> a_func)
        {
            return FindRoot(a_a, a_b, a_func, 
                CentralDifferenceMethod.Derive1(a_func, AbsoluteError));
        }

        public override double FindRoot(double a_a, double a_b, 
            Func<double, double> a_func, Func<double, double> a_d1, 
            Func<double, double> a_d2)
        {
            return FindRoot(a_a, a_b, a_func, a_d1);
        }

        public override double FindRoot(double a_a, double a_b, 
            Func<double, double> a_func, Func<double, double> a_d1)
        {
            return FindRootFromBothSide(a_a, a_b, a_func, 
                (s) => FindRoot(s, a_func, a_d1));
        }

        private RootState FindRoot(double a_x, Func<double, double> a_func,
            Func<double, double> a_d1)
        {
            double prev_y = a_func(a_x);

            for (int i = 0; i < MaxIterations; i++)
            {
                double dy = a_d1(a_x);
                if (dy.IsAlmostEquals(0))
                    return new RootState(a_x);

                a_x = a_x - prev_y / dy;
                double y = a_func(a_x);
                prev_y = y;

                if (Math.Abs(a_x) > AbsoluteError)
                {
                    if (Math.Abs(y / a_x) < AbsoluteError)
                        return new RootState(a_x);
                }
                else if (Math.Abs(y) < AbsoluteError)
                    return new RootState(a_x);
            }

            return new RootState(a_x, false);
        }

        public override double FindRoot(double a_a, double a_b, 
            Polynomial a_poly)
        {
            Polynomial d1 = a_poly.Differentiate();
            return FindRoot(a_a, a_b, (x) => a_poly.Evaluate(x),
                (x) => d1.Evaluate(x));
        }
    }
}
